
'''获取涨幅特征'''

def mark_raise(df):
    raise_df = df.copy()

    # 预排序避免重复排序开销
    raise_df = raise_df.sort_values(['code', 'date'])
    raise_df['date'] = pd.to_datetime(raise_df['date'])
    
    raise_df['prev_close'] = raise_df.groupby('code')['close'].shift(1)
    raise_df['prev_high'] = raise_df.groupby('code')['high'].shift(1)
    raise_df['max_gain'] = (raise_df['high'] - raise_df['prev_close']) / raise_df['prev_close']
    raise_df['max_drop'] = (raise_df['low'] - raise_df['prev_close']) / raise_df['prev_close']

    # 修改后的核心计算逻辑
    # 条件1：判断是否跳空高开 (open > prev_high)
    is_gap_up = raise_df['low'] > raise_df['prev_high']
    
    # 条件2：仅当跳空高开时计算幅度，否则赋0
    raise_df['open_gap_ratio'] = np.where(
        is_gap_up,  # 条件
        (raise_df['open'] - raise_df['prev_close']) / raise_df['prev_close'] * 100,  # 满足条件时的计算
        0  # 不满足条件时赋0
    )
    

    close_groups = raise_df.groupby('code')['close']
    
    # 生成特征d1、d2、d5、d10、d15.
    periods = [1,2,5,10,15]
    for p in periods:
        raise_df[f'r{p}'] = close_groups.pct_change(p).mul(100)
    
    def _cal_gt_flags(g):
        current_code = g.name
        processed = g.assign(
            code=current_code,  # 重建code列
            gt4=lambda x: x['quote_rate'].rolling(10, min_periods=1).max() > 4,
            gt7=lambda x: x['quote_rate'].rolling(10, min_periods=1).max() > 7,
            gt9=lambda x: x['quote_rate'].rolling(10, min_periods=1).max() > 9
        )
        return processed

    # 生成特征gt4，gt7、gt9。（10日内是否出现过大于4,7,9）
    raise_df = (
        raise_df.groupby('code', group_keys=True)  # 保留分组键元信息
        .apply(_cal_gt_flags, include_groups=False)  # 显式排除分组列
        .reset_index(drop=True)  # 清除残留的层级索引
    )
    # 涨停相关计算 (当天)
    raise_df['is_zt'] = (raise_df['close'] == raise_df['high_limit']).astype('bool')
    raise_df['is_gt4'] = (raise_df['quote_rate'] > 4).astype('bool')
    raise_df['is_gt7'] = (raise_df['quote_rate'] > 7).astype('bool')
    raise_df['is_gt9'] = (raise_df['quote_rate'] > 9).astype('bool')

    raise_df['zt_num'] = raise_df.groupby('code')['is_zt'].transform(
        lambda s: s.rolling(10, min_periods=1).sum().astype('int64')
    )
    raise_df['zt_num_20'] = raise_df.groupby('code')['is_zt'].transform(
    lambda s: s.rolling(20, min_periods=1).sum().astype('int64')
    )
    
    def _cal_month_raise(g):
        g['month'] = g['date'].dt.to_period('M')
        first_close = g.groupby('month')['close'].transform('first')
        first_quote = g.groupby('month')['quote_rate'].transform('first')
        g['m_raise'] = (g['close'] - first_close)/first_close *100 + first_quote
        return g.drop(columns='month')

    raise_df['gt4_num'] = raise_df.groupby('code')['is_gt4'].transform(
        lambda s: s.rolling(10, min_periods=1).sum().astype('int64')
    )
    raise_df['gt7_num'] = raise_df.groupby('code')['is_gt7'].transform(
        lambda s: s.rolling(10, min_periods=1).sum().astype('int64')
    )
    raise_df['gt9_num'] = raise_df.groupby('code')['is_gt9'].transform(
        lambda s: s.rolling(10, min_periods=1).sum().astype('int64')
    )


    def _cal_yesterday_feature(df):
        # 按股票分组并确保日期升序排列
        df = df.sort_values(by=['code', 'date'])
        # 计算昨日涨幅：将r1滞后1天
        df['pre_r1'] = df.groupby('code')['r1'].shift(1)
        df['pre_max_gain'] = df.groupby('code')['max_gain'].shift(1)
        df['pre_max_drop'] = df.groupby('code')['max_drop'].shift(1)
        df['pre_t_rate'] = df.groupby('code')['t_rate'].shift(1) #昨日换手率

        return df
    raise_df = raise_df.groupby('code', group_keys=False).apply(_cal_yesterday_feature)

    
    # 构建四级信号体系（条件优先级从高到低）
    conditions = [
        raise_df['quote_rate'] >= 9,                        # 条件1：涨幅≥9%
        raise_df['quote_rate'].between(4, 9, inclusive="left"),  # 条件2：4% ≤ 涨幅 <9%
        (raise_df['quote_rate'] < 4) & (raise_df['gt7_num'] >0)  # 条件3：<4%但历史存在>7%
    ]
    choices = [1, 2, 3]
    raise_df['raise_buy'] = np.select(conditions, choices, default=4)
    # 保留其他必要计算（月涨幅、涨停数等）
    raise_df = raise_df.groupby('code', group_keys=False).apply(_cal_month_raise)
    raise_df =  calculate_weekly_raise2(raise_df)

    raise_df[['gt4', 'gt9', 'is_zt']] = raise_df[['gt4', 'gt9', 'is_zt']].astype(bool)

    raise_df.drop(['prev_close','prev_high'], axis=1,errors='ignore', inplace=True)
    raise_df = drop_base_columns(raise_df)
    return raise_df.round(2)




